package controller;

import java.io.*;
import java.net.*;
import java.util.LinkedList;

/**
 * this class will configure the network in the game
 * @author Jose Luis Garcia && Alvaro Blazquez Checa
 *
 */
public class NetworkHandler
{	
	private Socket socket;
	private IncomingMessageHandler incomingMessageHandler;
	private OutgoingMessageHandler outgoingMessageHandler;
	private NetworkListener listener;
	public boolean connected;
	
	
	/**
	 * this construct create the param for make comunications between server and client
	 * @param address
	 * @param port
	 * @param listener
	 */
	public NetworkHandler(String address, int port, NetworkListener listener)
	{
		try
		{
			socket = new Socket(address,port);
			
			this.listener = listener;
			ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
			ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
			
			incomingMessageHandler = new IncomingMessageHandler(in);
			outgoingMessageHandler = new OutgoingMessageHandler(out);
			
			connected = true;
		}
		catch(IOException e)
		{
			System.out.println("Error al conectar" + e);
			listener.networkDisconnected();
		}
	}
	
	/**
	 * this will end the connection between server and client
	 */
	public synchronized void disconnect()
	{
		if(connected)
		{
			connected = false;	
			this.incomingMessageHandler.destroy();
			this.outgoingMessageHandler.destroy();
			
			try
			{
				socket.close();
			}
			catch(IOException e){}
			socket= null;
			
			listener.networkDisconnected();
		}
	}
	
	/**
	 * this will send a message to the server 
	 * @param message
	 */
	public void sendMessage(Object message)
	{
		outgoingMessageHandler.addMessage(message);
	}
	
	//INCOMING_MESSAGE_HANDLER
	/**
	 * this class will handle incoming Message from server
	 * @author Jose Luis Garcia && Alvaro Blazquez Checa
	 *
	 */
	public class IncomingMessageHandler implements Runnable
	{
		private ObjectInputStream inStream;
		private boolean running;
		/**
		 * this construct set the incoming handler
		 * @param in
		 */
		public IncomingMessageHandler(ObjectInputStream in)
		{
			inStream = in;
			new Thread(this).start();
		}
		/**
		 * this will start a network listen for params
		 */
		public void run()
		{
			running = true;
			while (running)
			{
				try
				{
					Object message = inStream.readObject();
					listener.networkMessageReceived(message);
				}
				catch(IOException | ClassNotFoundException e)
				{
					System.out.println(e);
					disconnect();
				}
			}
		}
		/**
		 * this will stop the handler
		 */
		public void destroy()
		{
			running = false;
		}
	}
	//INCOMING_MESSAGE_HANDLER
	
	
	//OUTGOING_MESSAGE_HANDLER
	/**
	 * this class will handle Outgoing Message for server
	 * @author Jose Luis Garcia && Alvaro Blazquez Checa
	 *
	 */
	public class OutgoingMessageHandler implements Runnable
	{
		private ObjectOutputStream outStream;
		private boolean running;
		LinkedList<Object> messageList;
		/**
		 *  this construct set the Outgoing handler
		 * @param out
		 */
		public OutgoingMessageHandler(ObjectOutputStream out)
		{
			outStream = out;
			messageList = new LinkedList<Object>();
			new Thread(this).start();
		}
		/**
		 * this will add message to be send to the server
		 * @param message
		 */
		public void addMessage(Object message)
		{
			synchronized(messageList)
			{
				messageList.add(message);
				messageList.notify();
			}
		}
		/**
		 * this will send the message
		 */
		public void run()
		{
			Object message;
			
			running = true;
			while(running)
			{
				synchronized(messageList)
				{
					if(messageList.isEmpty() && running)
					{
						try
						{
							messageList.wait();
						}
						catch(InterruptedException e) {}
					}
				}
				
				while(messageList.size()>0)
				{
					synchronized(messageList)
					{
						message = messageList.removeFirst();
					}
					try
					{
						outStream.writeObject(message);
					}
					catch(IOException e)
					{
						disconnect();
					}
				}
			}
		}
		/**
		 * this will destroy the message
		 */
		public void destroy()
		{
			running=false;
			
			synchronized(messageList)
			{
				messageList.notify();
			}
		}
	}
	//OUTGOING_MESSAGE_HANDLER
}
